#include <objc/runtime.h>

/*
__text:00000001001C07AC             ; void __cdecl -[KeybagSyncOperation run](KeybagSyncOperation *self, SEL)
__text:00000001001C07AC             __KeybagSyncOperation_run_
__text:00000001001C07AC FC 6F BA A9                 STP             X28, X27, [SP,#-0x60]!
__text:00000001001C07B0 FA 67 01 A9                 STP             X26, X25, [SP,#0x10]
//...
__text:00000001001C0868 68 0E 00 B0                 ADRP            X8, #selRef_unsignedLongLongValue@PAGE
__text:00000001001C086C 01 E1 45 F9                 LDR             X1, [X8,#selRef_unsignedLongLongValue@PAGEOFF]
__text:00000001001C0870 E0 03 14 AA                 MOV             X0, X20
__text:00000001001C0874 94 2A 03 94                 BL              _objc_msgSend
__text:00000001001C0878 61 01 80 52                 MOV             W1, #0xB
__text:00000001001C087C 08 9E F9 97                 BL              sub_10002809C
__text:00000001001C0880 F5 03 00 AA                 MOV             X21, X0
*/
void adc_main() {
  printf("Start to calc kbsync.\n");

  auto KeybagSyncOperation = NSClassFromString(@"KeybagSyncOperation");
  printf("Get KeybagSyncOperation class: %p.\n", KeybagSyncOperation);
  
  auto method = class_getInstanceMethod(KeybagSyncOperation, 
    NSSelectorFromString(@"run"));
  printf("Get run method: %p.\n", method);
  
  auto imp = method_getImplementation(method);
  printf("Get run implementation: %p.\n", imp);
  
  const uint32_t *kbsync_caller = (uint32_t *)imp;
  const uint8_t mov_w1_0xb[] = {
    0x61, 0x01, 0x80, 0x52
  };
  while (*kbsync_caller++ != *(uint32_t *)&mov_w1_0xb[0]); 
  printf("Parsed kbsync caller: %p.\n", kbsync_caller);

  // decode the bl instruction to get the real kbsyn callee
  // 31 30 29 28 27 26 25 ... 0
  //  1  0  0  1  0  1  - imm -
  int blopcode = *(int *)kbsync_caller;
  int blmask = 0xFC000000;
  if (blopcode & (1 << 26)) {
    // sign extend
    blopcode |= blmask;
  }
  else {
    blopcode &= ~blmask;
  }
  long kbsync_entry = (long)kbsync_caller + (blopcode << 2);
  printf("Decoded kbsync entry: 0x%lx.\n", kbsync_entry);

  // call the kbsync calc entry
  NSData *kbsync = ((NSData *(*)(long, int))kbsync_entry)(1111, 0xB);
  // send result to our python callback
  if (kbsync) {
    printf("kbsync_result_callback : %s.\n", [kbsync base64EncodedStringWithOptions:0].UTF8String);
    // str2py("kbsync_result_callback", [kbsync base64EncodedStringWithOptions:0].UTF8String, 0);
  }
  else {
    printf("kbsync_result_callback : error, you should download something in the AppStore to init kbsync.\n");
    // str2py("kbsync_result_callback", "error, you should download something in the AppStore to init kbsync.", 0);
  }
  printf("Finished calc kbsync.\n");
}
